package com.yueyang.sojourn.server.common;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

/**
 * @author yemingxing
 * @summary 用于查找已注册的Spring Bean，主要用于消除Spring基于代理实现的AOP this.method()调用不生效的缺陷；
 * @Copyright (c) 2020, Ke Group All Rights Reserved.
 * @since 2020-04-09 10:46
 */
public class Services {

    private static ApplicationContext delegateContext;

    static void setContext(ApplicationContext applicationContext) {
        delegateContext = applicationContext;
    }

    /**
     * 此方法只能查询到单个类型为{@code cls}的Bean，如果某个class有多个实现类被Spring托管，则会报错；
     *
     * @param clazz Bean的类型，不能为null
     */
    public static <T> T of(Class<T> clazz) {
        return delegateContext.getBean(clazz);
    }

    /**
     * 此方法只能查询到单个类型为{@code bean#getClass}的Bean，如果某个class有多个实现类被Spring托管，则会报错；
     *
     * @param bean Spring管理的Bean，不能为null
     */
    @SuppressWarnings("unchecked")
    public static <T> T of(T bean) {
        return (T) delegateContext.getBean(bean.getClass());
    }


    /**
     * 查找特定类型、特定名字的Spring Bean
     *
     * @param beanName Bean的名字
     * @param cls      期望返回的类型
     * @author Huisman
     */
    public static <T> T withName(String beanName, Class<T> cls) {
        return delegateContext.getBean(beanName, cls);
    }

    @Configuration
    static class ServicesConfig implements ApplicationContextAware {
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            // 注意： 由于依赖Spring初始化Bean的顺序，可能会导致先初始化的Bean调用Services.of时会返回null.
            // 不过大部分项目无需担心这个问题，主要是定时任务之类的有可能会出现ApplicationContext正在初始化Bean，部分已初始化的
            // 项目已经跑起来了。
            // 优化思路： 监听context loaded?
            Services.setContext(applicationContext);
        }
    }


}
